use darling::FromDeriveInput;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as T2;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};

use crate::periph2::rcc::{self, RccAxB};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(can))]
pub struct CanConfig {
    init: String,
    baud_rate: u32,
    fb_count: u8,
}

impl CanConfig {
    pub fn build(&self) -> T2 {
        let rcc_src = rcc::rcc_enable(RccAxB::APB1, &format_ident!("canen"), false);
        let init_fn = format_ident!("{}", self.init);

        let brp: u16 = match self.baud_rate {
            1000000 => 5,
            500000 => 11,
            250000 => 23,
            125000 => 47,
            _ => panic!("invalid baud_rate"),
        };
        let mut fa1r = Vec::new();
        let mut fs1r = Vec::new();
        let mut fm1r = Vec::new();
        let mut ffa1r = Vec::new();
        let mut fb = quote! {};
        for i in 0..self.fb_count {
            fa1r.push(format_ident!("fact{}", i));
            fs1r.push(format_ident!("fsc{}", i));
            fm1r.push(format_ident!("fbm{}", i));
            ffa1r.push(format_ident!("ffa{}", i));

            let index = i as usize;
            let value = format_ident!("FB{}_VALUE", i);
            let mask = format_ident!("FB{}_MASK", i);

            fb.extend(quote! {
                can.fb[#index].fr1.write(|w| w.fb().variant(Self::#value));
                can.fb[#index].fr2.write(|w| w.fb().variant(Self::#mask));
            });
        }

        quote! {
            pub fn can_read() -> (u32, u8, [u8; 8]) {
                let can: stm32f1::stm32f103::CAN1 = unsafe { ::core::mem::transmute(()) };
                let id = can.rx[0].rir.read().bits() >> 3;
                let len = can.rx[0].rdtr.read().dlc().bits();
                let tmp0 = can.rx[0].rdlr.read().bits();
                let tmp1 = can.rx[0].rdhr.read().bits();
                can.rfr[0].write(|w| w.rfom().release());
                (
                    id, 
                    len,
                    [
                        tmp0 as u8,
                        (tmp0 >> 8) as u8,
                        (tmp0 >> 16) as u8,
                        (tmp0 >> 24) as u8,
                        tmp1 as u8,
                        (tmp1 >> 8) as u8,
                        (tmp1 >> 16) as u8,
                        (tmp1 >> 24) as u8,
                    ]
                )
            }

            pub fn can_write_std_frame(id: u32, ptr: *const u8, len: u8) {
                let can: stm32f1::stm32f103::CAN1 = unsafe { ::core::mem::transmute(()) };

                can.tx[0].tdtr.write(|w| w.dlc().variant(len));

                let tmp0 = unsafe {
                    (ptr.read() as u32)
                        | ((ptr.add(1).read() as u32) << 8)
                        | ((ptr.add(2).read() as u32) << 16)
                        | ((ptr.add(3).read() as u32) << 24)
                };
            
                let tmp1 = unsafe {
                    (ptr.add(4).read() as u32)
                        | ((ptr.add(5).read() as u32) << 8)
                        | ((ptr.add(6).read() as u32) << 16)
                        | ((ptr.add(7).read() as u32) << 24)
                };
            
                can.tx[0].tdlr.write(|w| unsafe { w.bits(tmp0) });
                can.tx[0].tdhr.write(|w| unsafe { w.bits(tmp1) });
                can.tx[0].tir.write(|w| unsafe { 
                    w.bits((id << 21) | 0x01) 
                });
            }

            pub fn can_write_ext_frame(id: u32, ptr: *const u8, len: u8) {
                let can: stm32f1::stm32f103::CAN1 = unsafe { ::core::mem::transmute(()) };

                can.tx[0].tdtr.write(|w| w.dlc().variant(len));

                let tmp0 = unsafe {
                    (ptr.read() as u32)
                        | ((ptr.add(1).read() as u32) << 8)
                        | ((ptr.add(2).read() as u32) << 16)
                        | ((ptr.add(3).read() as u32) << 24)
                };
            
                let tmp1 = unsafe {
                    (ptr.add(4).read() as u32)
                        | ((ptr.add(5).read() as u32) << 8)
                        | ((ptr.add(6).read() as u32) << 16)
                        | ((ptr.add(7).read() as u32) << 24)
                };
            
                can.tx[0].tdlr.write(|w| unsafe { w.bits(tmp0) });
                can.tx[0].tdhr.write(|w| unsafe { w.bits(tmp1) });
                can.tx[0].tir.write(|w| unsafe { 
                    w.bits((id << 3) | 0x04 | 0x01) 
                });
            }

            pub fn #init_fn() {
                #rcc_src
                let can: stm32f1::stm32f103::CAN1 = unsafe { ::core::mem::transmute(()) };
                can.mcr.write(|w| w
                    .inrq().set_bit()
                    .abom().set_bit()
                    .nart().set_bit()
                );
                while can.msr.read().inak().bit_is_clear() {}

                const TBS2: u8 = 2;
                const TBS1: u8 = 1;
                const BRP: u16 = #brp;

                can.btr.write(|w| w
                    .ts2().variant(TBS2)
                    .ts1().variant(TBS1)
                    .brp().variant(BRP)
                );
                can.mcr.modify(|_, w| w.inrq().clear_bit());
                while can.msr.read().inak().bit_is_set() {}

                can.fmr.write(|w| w.finit().set_bit());

                can.fa1r.write(|w| w #(.#fa1r().clear_bit())* ); // 过滤器禁用
                can.fs1r.write(|w| w #(.#fs1r().set_bit())* ); // 单个32位
                can.fm1r.write(|w| w #(.#fm1r().clear_bit())* ); // 标识符屏蔽模式
                can.ffa1r.write(|w| w #(.#ffa1r().clear_bit())* ); // 关联到FIFO 0
                #fb

                can.fa1r.write(|w| w #(.#fa1r().set_bit())* ); // 过滤器启用
                can.fmr.write(|w| w.finit().clear_bit());

                can.ier.write(|w| w.tmeie().enabled().fmpie0().enabled());
            }
        }
    }
}

pub fn build(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let config = CanConfig::from_derive_input(&input).unwrap();
    let name = &input.ident;
    let build = config.build();
    let expand = quote! {

        impl #name {
            #build
        }

    };
    expand.into()
}
